#include <iostream>
#include <fstream>
#include <queue>
#include <list>
#include <sstream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/tr1/memory.hpp>

#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>

#include <ctime>
#include <cstdio>

#include "URLDiskClass.h"
#include "../classes_net/URL.h"

using namespace std;

URLDiskClass::URLDiskClass(){
//	diskIO.open("fileTeste.txt", ios::in | ios::out | ios::binary); //ios::in | ios::out | ios::binary
//	if(diskIO == NULL) cout << "Erro.. " << endl;
	pDisk[0] = fopen64 ("DiscoURL_P0.bin","ab+");  //Leitura e "escrita no final"
	pDisk[1] = fopen64 ("DiscoURL_P1.bin","ab+");  //Leitura e "escrita no final"
	pDisk[2] = fopen64 ("DiscoURL_P2.bin","ab+");  //Leitura e "escrita no final"
	pDisk[3] = fopen64 ("DiscoURL_P3.bin","ab+");  //Leitura e "escrita no final"
	pDisk[4] = fopen64 ("DiscoURL_P4.bin","ab+");  //Leitura e "escrita no final"
	pDisk[5] = fopen64 ("DiscoURL_P5.bin","ab+");  //Leitura e "escrita no final"
	
	for(int i=0; i<6; i++)
	    if (pDisk[i] == NULL){
	    	cout << "Erro ao abrir o arquivo de disco pDisk: "<< i << endl;
	    	exit(1);
	    }
	
    pSaveDiskReadPos[0] = fopen64 ("pSaveDiskReadPos_P0","r+");  //Leitura e escreve
    pSaveDiskReadPos[1] = fopen64 ("pSaveDiskReadPos_P1","r+");  //Leitura e escreve
    pSaveDiskReadPos[2] = fopen64 ("pSaveDiskReadPos_P2","r+");  //Leitura e escreve
    pSaveDiskReadPos[3] = fopen64 ("pSaveDiskReadPos_P3","r+");  //Leitura e escreve
    pSaveDiskReadPos[4] = fopen64 ("pSaveDiskReadPos_P4","r+");  //Leitura e escreve
    pSaveDiskReadPos[5] = fopen64 ("pSaveDiskReadPos_P5","r+");  //Leitura e escreve
    
    for(int i=0; i<6; i++)
	    if (pSaveDiskReadPos[i] == NULL){
	    	cout << "Erro ao abrir o arquivo de disco pSaveDiskReadPos: " << i << endl;
	    	exit(1);
	    }
	    
    
//    cout << "Aqui, em URLDiskClass() "<< endl;
    
    URLBuffer_IN = new _t_URL;
    URLBuffer_OUT = new _t_URL;
    
//    cout << "Ainda em URLDiskClass() "<< endl;
}

URLDiskClass::~URLDiskClass(){
	//	diskIO.close();
//	 cout << "Aqui, em ~URLDiskClass() "<< endl;
	 
	for(int i=0; i<6; i++)	 
		if(pDisk[i] != NULL) fclose (pDisk[i]);
	
	for(int i=0; i<6; i++)
		if(pSaveDiskReadPos[i] != NULL) fclose (pSaveDiskReadPos[i]);
	
	delete URLBuffer_IN;
	delete URLBuffer_OUT;
}


void URLDiskClass::writeDisk(URL t){
	boost::mutex::scoped_lock urlDiskClassLock(urlDiskClassMutex);
//	URLBuffer_OUT = new _t_URL;
// Estrutura da URL:
//		std::string path;
//		std::string dominio;
//		std::string protocol;	
//		short depth;  ---> Nao precisa salvar !!!
	try{
		
		string _path;
		string _dominio;
		string _protocolo;
		_path = (t.get_path().size() >= 230) ? t.get_path().substr(0,229) : t.get_path();
		_dominio = (t.get_dominio().size() >= 60) ? t.get_dominio().substr(0,59) : t.get_dominio();
		_protocolo = (t.get_protocol().size() >= 10) ? t.get_protocol().substr(0,9) : t.get_protocol();
		
		//cout << "Aqui, URLDiskClass::writeDisk() " << endl;
		strcpy(URLBuffer_OUT->t_path, _path.c_str());
		strcpy(URLBuffer_OUT->t_dominio, _dominio.c_str());
		strcpy(URLBuffer_OUT->t_protocol, _protocolo.c_str());
	
		int res;
		int nivel;
		
		//coloca no disco correto
		nivel = t.getDepth();
		if(nivel < 0) nivel = 0;
		if(nivel > 5) nivel = 5;
		
		res = fwrite(URLBuffer_OUT, sizeof(_t_URL), 1, pDisk[nivel] );
	
		if(res != 1){
			cout << "Erro ao gravar em URLDiskClass::writeDisk() " <<  endl;
		}
		else{
			cout << "Gravou no disco: " << nivel << endl;
			cout << "URL: " << t.toString() << endl;
			cout << "Tamanho do disco atual: " << size(nivel) << endl;
			cout << "Pos do disco Atual: " << pos(nivel) << endl;	
			cout << "Tamanho Total Novo: " << totalNewURLs() << endl;
		}
		
		
//	    fflush (pDisk);    // flushing required
	//    cout << res << endl;
	}catch(...){
		cout << "Erro no URLDiskClass::writeDisk " << endl;
	}
//	delete URLBuffer_OUT;
}



URL URLDiskClass::readDisk(void){
	boost::mutex::scoped_lock urlDiskClassLock(urlDiskClassMutex);
//	URLBuffer_IN = new _t_URL;
//	SEEK_SET	Beginning of file
//	SEEK_CUR	Current position of the file pointer
//	SEEK_END	End of file
	long long pDiskOffset;
  	long long pDiskSize;
  	long long _savePos;
  	
  	int id;
  	for(id=0; id<6; id++){
  	  	// Tamanho do disco
  	  	pDiskSize = size(id);
  	  	//Recupera ultima posicao lida
  	  	_savePos  = pos(id);
  	  	//verifica se vazio
  	  	if(_savePos > pDiskSize) continue;
  	  	
  	  	pDiskOffset = _savePos;
  	  	_savePos = _savePos + 1;
  	  	
  	  	fseeko64 ( pDisk[id] , pDiskOffset*sizeof(_t_URL) , SEEK_SET);
  	  	fread(URLBuffer_IN, sizeof(_t_URL), 1, pDisk[id]);
  	  	tURL = URL(URLBuffer_IN->t_dominio, URLBuffer_IN->t_path, URLBuffer_IN->t_protocol );
  	  	
  	  	//Grava ultima pos lida
  	  	writepos(id, _savePos);
  	  
  	    return tURL;
  	}
  	  
	cout << "Todos os discos vazios..." << endl;
	tURL = URL("", "","");
	return tURL;  
//  delete URLBuffer_IN;
}




long long URLDiskClass::size(int id){
	long long pDiskSize;
	if(id < 0) id = 0;
	if(id > 5) id = 5;
  	fseeko64 (pDisk[id] , 0 , SEEK_END); //vai para o final
  	pDiskSize = ftello64 (pDisk[id]);  	// Pega o tamanho
  	pDiskSize = pDiskSize / sizeof(_t_URL);
  	return pDiskSize;
}


long long URLDiskClass::pos(int id){
	long long _save_pos;
	if(id < 0) id = 0;
	if(id > 5) id = 5;
  	rewind (pSaveDiskReadPos[id]);
  	fscanf (pSaveDiskReadPos[id], "%lld", &_save_pos);
  	return _save_pos;
}

void URLDiskClass::writepos(int id, long long _save_pos){
  	//Grava ultima pos lida
  	rewind (pSaveDiskReadPos[id]);
  	fprintf(pSaveDiskReadPos[id], "%lld", _save_pos); // aponta para proximo lugar a ser lido
  	fflush (pSaveDiskReadPos[id]);
}

long long URLDiskClass::totalNewURLs(void){
	long long tmTotal;
	long long tmTemp;
	
	tmTotal = 0;
	
	for(int i=0; i<6; i++){
		tmTemp = size(i) - pos(i);
		if(tmTemp >= 0) tmTotal += tmTemp;		
	}
	
	return tmTotal;
}



